package net.tyw.core.service.oauth.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import net.tyw.common.exception.ServiceException;
import net.tyw.common.paginator.domain.PageBounds;
import net.tyw.common.paginator.domain.PageResult;
import net.tyw.common.utils.DateUtil;
import net.tyw.common.utils.RandCodeUtil;
import net.tyw.core.auth.OauthService;
import net.tyw.core.enums.oauth.UserLoginAccountType;
import net.tyw.core.enums.oauth.UserPasswdType;
import net.tyw.core.mapper.oauth.ResourceMapper;
import net.tyw.core.mapper.oauth.RoleMapper;
import net.tyw.core.mapper.oauth.UserInfoMapper;
import net.tyw.core.model.oauth.*;
import net.tyw.core.service.BaseService;
import net.tyw.core.service.oauth.UserInfoService;
import net.tyw.core.vo.oauth.UserInfoVo;
import org.apache.commons.lang3.StringUtils;
import org.jsoup.Connection;
import org.jsoup.Jsoup;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;

import java.util.Collections;
import java.util.List;

@Service
public class UserInfoServiceImpl extends BaseService implements UserInfoService {
    private final static Logger logger = LoggerFactory.getLogger(UserInfoServiceImpl.class);

    @Autowired
    private UserInfoMapper userInfoMapper;

    @Autowired
    private ResourceMapper resourceMapper;

    @Autowired
    private RoleMapper roleMapper;

    @Autowired
    private ThreadPoolTaskExecutor threadPoolTaskExecutor;

    @Autowired
    private OauthService oauthService;

    @Override
    public void register(UserInfo userInfo, String email, String mp, String loginPassword, List<Long> roles) throws ServiceException {
        Exception exception = this.transactionTemplate.execute(status -> {
            try {
                userInfoMapper.insertUserInfo(userInfo);

                String salt = RandCodeUtil.getSalt();

                UserPassword userPassword = new UserPassword();
                userPassword.setPasswd(oauthService.genPwd(loginPassword, salt, true));
                userPassword.setSalt(salt);
                userPassword.setType(UserPasswdType.login);
                userPassword.setUid(userInfo.getId());
                userInfoMapper.insertPassword(userPassword);

                userInfoMapper.insertLoginAccount(userInfo.getId(), email.toLowerCase(), UserLoginAccountType.email);
                userInfoMapper.insertLoginAccount(userInfo.getId(), mp, UserLoginAccountType.mp);
                if (roles != null && roles.size() > 0) {
                    userInfoMapper.insertUserRole(userInfo.getId(), roles);
                }
            } catch (Exception e) {
                status.setRollbackOnly();
                return e;
            }
            return null;
        });

        if (exception != null) {
            throw new ServiceException(exception);
        }
    }

    @Override
    public UserLoginAccount findLoginAccount(String loginAccount, UserLoginAccountType accountType) {
        return this.userInfoMapper.findLoginAccount(loginAccount.toLowerCase(), accountType);
    }

    @Override
    public List<UserLoginAccount> findAllLoginAccount(Long uid) {
        return this.userInfoMapper.findAllLoginAccount(uid);
    }

    @Override
    public UserInfo findUserInfoById(Long id) {
        return this.userInfoMapper.findUserInfoById(id);
    }

    @Override
    public UserPassword findUserPassword(Long uid, UserPasswdType userPasswdType) {
        return this.userInfoMapper.findUserPassword(uid, userPasswdType);
    }

    @Override
    public UserLoginLog findLastLogin(Long uid) {
        return this.userInfoMapper.findLastLogin(uid);
    }

    @Override
    public void updateLoginInfo(Long uid, String remoteIp) {
        this.threadPoolTaskExecutor.execute(() -> transactionTemplate.execute(status -> {
            try {
                UserLoginLog loginLog = new UserLoginLog();
                try {
                    Connection connection = Jsoup.connect("http://ip.taobao.com/service/getIpInfo.php").ignoreContentType(true);
                    connection = connection.data("ip", remoteIp);
                    String result = connection.get().text();
                    JSONObject jsonObject = JSON.parseObject(result);
                    if (jsonObject.getInteger("code").equals(0)) {
                        loginLog = jsonObject.getObject("data", UserLoginLog.class);
                    }
                } catch (Exception e) {
                    logger.warn("iplookup error ");
                }
                loginLog.setLoginTime(DateUtil.current());
                loginLog.setLoginIp(remoteIp);
                loginLog.setUid(uid);
                userInfoMapper.insertLoginLog(loginLog);
            } catch (Exception e) {
                status.setRollbackOnly();
                logger.warn("updateLoginInfo error", e);
            }
            return null;
        }));

    }

    @Override
    public void updateUserPassword(Long uid, String passwd, String salt, UserPasswdType userPasswdType) {
        UserPassword userPassword = this.userInfoMapper.findUserPassword(uid, userPasswdType);
        if (userPassword == null) {
            userPassword = new UserPassword();
            userPassword.setPasswd(passwd);
            userPassword.setUid(uid);
            userPassword.setSalt(salt);
            userPassword.setType(userPasswdType);
            this.userInfoMapper.insertPassword(userPassword);

        } else {
            this.userInfoMapper.updateUserPassword(uid, passwd, salt, userPasswdType);
        }
    }

    @Override
    public PageResult<UserInfo> findPage(PageBounds bounds, UserInfoVo vo) {
        return this.userInfoMapper.findPage(bounds, vo).getPageResult();
    }

    @Override
    public void updateUserInfo(UserInfo userInfo, String loginPassword, List<Long> roles) throws ServiceException {

        Exception exception = this.transactionTemplate.execute(status -> {
            try {
                userInfoMapper.updateUserInfo(userInfo);

                if (StringUtils.isNotBlank(loginPassword)) {
                    String salt = RandCodeUtil.getSalt();
                    String passwd = oauthService.genPwd(loginPassword, salt, true);

                    updateUserPassword(userInfo.getId(), passwd, salt, UserPasswdType.login);
                }
                if (roles != null && roles.size() > 0) {
                    userInfoMapper.deleteUserRole(userInfo.getId());
                    userInfoMapper.insertUserRole(userInfo.getId(), roles);
                }
            } catch (Exception e) {
                status.setRollbackOnly();
                return e;
            }
            return null;
        });

        if (exception != null) {
            throw new ServiceException(exception);
        }
    }

    @Override
    public List<Resource> findAllResource(Long parentId) {
        List<Resource> listResource = this.resourceMapper.findAllResource(parentId);
        if (parentId.compareTo(0L) == 0) {
            for (Resource resource : listResource) {
                resource.setChildren(this.resourceMapper.findAllResource(resource.getId()));
            }
        }
        return listResource;
    }

    @Override
    public List<Resource> findResourceByRole(Long roleId) {
        return this.resourceMapper.findResourceByRole(roleId);
    }

    @Override
    public List<Resource> findResourceByUser(Long uid) {
        return this.resourceMapper.findResourceByUser(uid);
    }

    @Override
    public List<RoleInfo> findAllRole() {
        return this.roleMapper.findAllRole();
    }

    @Override
    public List<RoleInfo> findRoleByUser(Long uid) {
        return this.roleMapper.findRoleByUser(uid);
    }

    @Override
    public RoleInfo findRoleById(Long roleId) {
        return this.roleMapper.findById(roleId);
    }

    @Override
    public void saveRole(RoleInfo roleInfo, List<Long> listResourceIds) throws ServiceException {

        Exception exp = this.transactionTemplate.execute(status -> {
            try {
                if (roleInfo.getId() == null) {
                    this.roleMapper.insert(roleInfo);
                } else {
                    this.roleMapper.update(roleInfo);
                    List<Long> listRoleIds = Collections.singletonList(roleInfo.getId());
                    this.roleMapper.deleteRoleResource(listRoleIds);
                }
                if (listResourceIds != null && listResourceIds.size() > 0) {
                    this.roleMapper.insertRoleResource(roleInfo.getId(), listResourceIds);
                }
                return null;
            } catch (Exception e) {
                status.setRollbackOnly();
                return e;
            }
        });

        if (exp != null) {
            throw new ServiceException(exp);
        }
    }

    @Override
    public void deleteRole(List<Long> listRoleIds) throws ServiceException {
        Exception exp = this.transactionTemplate.execute(status -> {
            try {
                roleMapper.delete(listRoleIds);
                roleMapper.deleteRoleResource(listRoleIds);
                return null;
            } catch (Exception e) {
                status.setRollbackOnly();
                return e;
            }
        });
        if (exp != null) {
            throw new ServiceException(exp);
        }
    }

}
